/**
 * Copyright 2011 Honeywell Federal Manufacturing & Technologies, LLC. This material was produced under U.S. Government
 * contract DE-ACO4-01AL66850, Honeywell Federal Manufacturing & Technologies, LLC, (FM&T) with the U. S. Department of
 * Energy for the operation of the Kansas City Plant . The U.S. Government has rights to use, reproduce, and distribute
 * this software.  NEITHER THE GOVERNMENT NOR Honeywell Federal Manufacturing & Technologies, LLC, MAKES ANY WARRANTY,
 * EXPRESS OR IMPLIED, OR ASSUMES ANY LIABILITY FOR THE USE OF THIS SOFTWARE.  If software is modified to produce
 * derivative works, such modified software should be clearly marked, so as not to confuse it with the version available
 * from FM&T.
 *
 * Additionally, this program is free software; you can redistribute it and/or modify it under the terms of the GNU
 * General Public License Version 3 published by the Free Software Foundation. Accordingly, this program is distributed in
 * the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License along with this library.
 * If not, see http://www.gnu.org/licenses/.
 */
package org.braintrain.services.util;

import com.kcp.ko.fmtnmlib.service.dao.HibernatePersistenceDAO;
import org.apache.tapestry.services.DataSqueezer;
import org.braintrain.domain.Navigation;
import org.braintrain.domain.ResourceReference;
import org.braintrain.domain.types.NavigationType;
import org.braintrain.services.MediaService;
import org.braintrain.services.util.domContentWorkers.*;
import org.braintrain.tapestry.encoders.ContentServiceEncoder;
import org.w3c.dom.Document;
import org.w3c.dom.NamedNodeMap;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;

import java.util.ArrayList;
import java.util.List;

/**
 * Parses a given HTML Document and checks a number of link tags.
 * These tags must reference an existing Media or Navigation (links or pop-ups) 
 * in the database or those tags are replace by the NOT_FOUND_PATH image.
 * 
 * @author John Ericksen
 *
 */

public class DOMContentParser {
    
    public final static String NOT_FOUND_PATH = "images/notFound.gif"; 
    
    private Document document;

    private List<ResourceReference> references;
    
    private Long navigationId;
    
    private MediaService mediaService;
    
    private HibernatePersistenceDAO genericDAO;
    
    private DataSqueezer dataSqueezer;
    
    private boolean allowTooltips;
    
    private boolean allowPageLinks;
    
    private List<AttributeValidator> attributeValidators;
    
    /**
     * The HTML Tag parameters that contain URLS
     */
    private final static String[] linkingAttributes = 
    {"href", "src", "onfocus", "onblur", 
        "onclick", "ondblclick", "onmousedown", 
        "onmouseup", "onmouseover", "onmousemove", 
        "onmouseout", "onkeypress", "onkeydown", "onkeyup"};

    public MediaService getMediaService() {
        return mediaService;
    }

    public final void setMediaService(MediaService mediaService) {
        this.mediaService = mediaService;
    }
    
    public DOMContentParser(Document document, Long navigationId, boolean allowPageLinks, boolean allowTooltips, HibernatePersistenceDAO genericDAO, MediaService mediaService, DataSqueezer dataSqueezer)
    {
        this.navigationId = navigationId;
        this.document = document;
        this.allowPageLinks = allowPageLinks;
        this.allowTooltips = allowTooltips;
        this.mediaService = mediaService;
        this.dataSqueezer = dataSqueezer;
        this.genericDAO = genericDAO;
        
        attributeValidators = new ArrayList<AttributeValidator>();

        attributeValidators.add(new AnchorValidator(this));
        attributeValidators.add(new ContentLinkValidator(this));
        attributeValidators.add(new DirectContentLinkValidator(this));
        attributeValidators.add(new MediaLinkValidator(this));
    }
    
    
    public Document linkNodes()
    {
        references = new ArrayList<ResourceReference>();
        
        recursiveLinkNodes(this.document);
        
        return this.document;
    }
    
    /**
     * check each node's children (recursive check) for the given linking tags (linkingAttributes)
     * as well as the special case of embeded Flash/movie/etc content
     * 
     * @param parent
     * @return
     */
    private void recursiveLinkNodes(Node parent)
    {
        if(parent != null)
        {
            NodeList nodes = parent.getChildNodes();
            
            for(int i = 0; i < nodes.getLength(); i++)
            {
                Node node = nodes.item(i);
                
                //check node for links
                if(node.hasAttributes())
                {
                    NamedNodeMap map = node.getAttributes();
                    Node attribute = null;
                    
                    for(String name: linkingAttributes)
                    {
                        attribute = map.getNamedItem(name);
                        
                        processAttribute(attribute);
                    }
                    
                    //special param case
                    if(node.getNodeName().equals("param"))
                    {
                        Node name = map.getNamedItem("name");
                        if(name.getNodeValue().equals("src"))
                        {
                            processAttribute(map.getNamedItem("value"));
                        }
                    }
                }
                
                recursiveLinkNodes(node);
            }
        }
    }
    
    /**
     * Checks a node for the appropriate allowable content.  Unless one of the attribute validators
     * returns true (as in they vouch for the correctness of the given attribute), 
     * the given node will be added to the remove list in recursiveLinkNode (and replacementNode())
     * 
     * @param attribute
     * @return
     */
    private void processAttribute(Node attribute)
    {

        for(AttributeValidator validator: attributeValidators)
        {
            validator.processAttribute(attribute);
        }
    }

    public String generatePagePath(Navigation linkNav){
        return ContentServiceEncoder.buildPagePath(linkNav.getType(), getDataSqueezer().squeeze(new Long[]{linkNav.getId(), navigationId}));
    }
    
    public String generatePagePath(){
        if(navigationId == null)
        {
            return "app";
        }
        return ContentServiceEncoder.buildPagePath(NavigationType.View, getDataSqueezer().squeeze(new Long[]{navigationId}));
    }

    public List<ResourceReference> getReferences() {
        return references;
    }

    public DataSqueezer getDataSqueezer() {
        return dataSqueezer;
    }

    public HibernatePersistenceDAO getGenericDAO() {
        return genericDAO;
    }

    public boolean isAllowTooltips() {
        return allowTooltips;
    }

    public boolean isAllowPageLinks() {
        return allowPageLinks;
    }

    public Long getNavigationId() {
        return navigationId;
    }
}
